package com.sxkj.service.redis;

import com.sxkj.common.utils.StringUtils;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author Zhang Xingliang
 * @description 分布式锁
 * @date 13:59 2019/12/24
 **/
@Service
public class RedlockService {

    private static final Logger logger = LoggerFactory.getLogger(RedlockService.class);

    @Autowired
    public RedissonClient redissonClient;

//    @Autowired
    public RLock lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        return lock;
    }

    /**
     * simple lock尝试获取锅的次数
     */
    private int retryCount = 3;

    /**
     * 每次尝试获取锁的重试间隔毫秒数
     */
    private int waitIntervalInMS = 100;

    /**
     * 利用Redisson获取分布式锁(未获取锁的请求，允许丢弃!)
     *
     * @param lockKey       锁的key值
     * @param waitTime 500ms拿不到锁, 就认为获取锁失败
     * @param leaseTime 锁的自动释放时间(秒)
     * @return
     * @throws Exception
     */
    public String simpleLock(final String lockKey, final int waitTime, final int leaseTime) throws Exception {
        String lockValue = UUID.randomUUID().toString();
        boolean flag = false;
        if (StringUtils.isEmpty(lockKey)) {
            throw new Exception("key is empty!");
        }
        if (leaseTime <= 0) {
            throw new Exception("leaseTime must be bigger than 0");
        }
        RLock lock = redissonClient.getLock(lockKey);
        RedissonRedLock redLock = new RedissonRedLock(lock);
        try {
            for (int i = 0; i < retryCount; i++) {
                // isLock = redLock.tryLock();
                // 500ms拿不到锁, 就认为获取锁失败。10000ms即10s是锁失效时间。
                boolean isLock = redLock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
                if (isLock) {
                    //TODO 获取到锁，处理相关业务
                    System.out.println(" 获取到锁，处理相关业务");
                    Thread.sleep(200);  //就按照每个业务处理平均200ms完成
                    //TODO if get lock success, do something;
                    flag = true;
                    break;
                }
                try {
                    TimeUnit.MILLISECONDS.sleep(waitIntervalInMS);
                } catch (Exception ignore) {
                    logger.warn("redis lock fail: " + ignore.getMessage());
                }
            }
            if (!flag) {
                throw new Exception(Thread.currentThread().getName() + " cannot acquire lock now ...");
            }
            return lockValue;
        } catch (Exception e) {
            logger.warn("get redis lock error, exception: " + e.getMessage());
            throw e;
        } finally {
            redLock.unlock();
        }
    }

    /**
     * 利用Redisson获取分布式锁(未获取锁的请求，将在timeoutSecond时间范围内，一直等待重试)
     *
     * @param lockKey       锁的key值
     * @param waitTime 锁的自动释放时间(秒)
     * @param leaseTime 锁的自动释放时间(秒)
     * @param timeoutSecond  未获取到锁的请求，尝试重试的最久等待时间(秒)
     * @return
     * @throws Exception
     */
    public String lock(final String lockKey, final int waitTime, final int leaseTime, final int timeoutSecond) throws Exception {
        String lockValue = UUID.randomUUID().toString();
        boolean flag = false;
        if (StringUtils.isEmpty(lockKey)) {
            throw new Exception("key is empty!");
        }
        if (waitTime <= 0) {
            throw new Exception("waitTime must be greater than 0");
        }
        if (timeoutSecond <= 0) {
            throw new Exception("timeoutSecond must be greater than 0");
        }
        if (timeoutSecond >= leaseTime) {
            throw new Exception("timeoutSecond must be less than leaseTime");
        }
        RLock lock = redissonClient.getLock(lockKey);
        RedissonRedLock redLock = new RedissonRedLock(lock);
        try {
            long timeoutAt = System.currentTimeMillis() + timeoutSecond * 1000;
            while (true) {
                boolean isLock = redLock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
                if (isLock) {
                    flag = true;
                    break;
                }
                if (System.currentTimeMillis() >= timeoutAt) {
                    break;
                }
                try {
                    TimeUnit.MILLISECONDS.sleep(waitIntervalInMS);
                } catch (Exception ignore) {
                    logger.warn("redis lock fail: " + ignore.getMessage());
                }
            }
            if (!flag) {
                throw new Exception(Thread.currentThread().getName() + " cannot acquire lock now ...");
            }
            return lockValue;
        } catch (Exception e) {
            logger.warn("get redis lock error, exception: " + e.getMessage());
            throw e;
        } finally {
            redLock.unlock();
        }
    }


    /**
     * 锁释放
     *
     * @param lockKey
     * @param lockValue
     */
    public void unlock(final String lockKey, final String lockValue) {
        if (StringUtils.isEmpty(lockKey)) {
            return;
        }
        if (StringUtils.isEmpty(lockValue)) {
            return;
        }
        try {

        } catch (Exception je) {
            logger.warn(Thread.currentThread().getName() + " unlock lockKey lock error:" + je.getMessage());
        }
    }
}
